#include <stdio.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <time.h>
#include <algorithm>

DWORD MAX_FILE_SIZE = 1024*1024*1024*2;

bool isNumber(const char& a)
{
  if(a <= '9' && a >= '0')
    return true;
  return false;
}

int charToInt(const char& a)
{
  if(isNumber(a))
    return a - 48;
  return -1;
}

std::string intToString(const int& i)
{
  std::ostringstream out;
  out << i;
  std::string temp_str = out.str() + " ";
  return temp_str;
}

int power(const int& number, const int& power)
{
  int result = 1;
  if(power == 0)
    return result;
  for(int i = 1; i <= power; i++)
    result *= 10;
  return result;
}

int stringToInt(const std::string str)
{
  int result = 0;
  for(int i = 0; i < str.length(); i++)
    result = result * 10 + charToInt(str[i]);
  return result;
}

//sort function
void insertionVectorSort(std::vector<int>& vector_for_sort) {
  sort(vector_for_sort.begin(), vector_for_sort.end());
 /* int tmp = 0;
  int j = 0;
  for (int i = 0; i < vector_for_sort.size(); ++i) {
    tmp = vector_for_sort[i];
    for (j = i - 1; j >= 0 && vector_for_sort[j] > tmp; --j)
      vector_for_sort[j + 1] = vector_for_sort[j];
    vector_for_sort[j + 1] = tmp;
  }*/
}

//function got from net. just for transforming
std::wstring s2ws(const std::string& s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

//bool createIntArrayFromChar(const char* input, int** return_array)
//{
//
//}

bool createVectorFromChar(const char* input, std::vector<int>& vector_to_sort)
{
  std::string current_word = "";
  std::cout << "Start creating vector\n";
  long current_size = 0;
  for(int i = 0; input[i] != NULL; i++)
  {
    if(isNumber(input[i]))
    {
      while(isNumber(input[i]) && input[i] != NULL)
      {
        current_word += input[i];
        i++;
      }
      i--;
      current_size++;
      if(current_size >= vector_to_sort.size())
      {
        vector_to_sort.resize(current_size*2, 1);
      }
      vector_to_sort[current_size-1] = stringToInt(current_word);

    }
    else if(input[i] != ' ' && input[i] != '\n')
      return false;
    current_word = "";
  }
  vector_to_sort.resize(current_size);
  std::cout << "End creating vector\n";
  return true;
}

void HandleLastError(const char *msg) {
        DWORD errCode = GetLastError();
        LPVOID err;
        if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                           FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,
                           errCode,
                           NULL, // default language
                           (LPTSTR)&err,
                           0,
                           NULL))
            return;
        std::wcout << msg << err << "\n";
        LocalFree(err);
}

int main(int argc, char* argv[])
{
  std::vector<long> duration_time;
  long start = time(0);
  long average = 0;
  if(argc != 3)
  {
    if(argc < 3)
      std::cout << "Not enough arguments" << "\n";
    else if(argc > 3)
      std::cout << "Too many arguments" << "\n";
    std::cout << "Program need 2 arguments: input data file and output data file where the program will put sorted array\n";
    return 0;
  }

  //<--------Reding from file
  
  std::string str(argv[1]);

  //creating file
  HANDLE inputFile = CreateFile(s2ws(str).c_str(),GENERIC_READ|GENERIC_WRITE,0,NULL, OPEN_EXISTING,0,NULL);
  
  //getting size of the file
  DWORD dwFileSizeHi = 0;
  DWORD fileSize = GetFileSize(inputFile, &dwFileSizeHi);

  if(GetLastError() != 0)
  {
    if(GetLastError() == ERROR_INVALID_HANDLE)
      //HandleLastError("File doesn't exist: "); <----tried to do it like this, but output was not good.
      //I believe problems with coding, but i'm not sure, because i've tried a lot of parameters
      std::cout << "File doesn't exist: "<< argv[1] <<"\n";
    else std::cout << "Error with openning file: " << argv[1] << "\n";
    return 0;
  }
  if(fileSize > MAX_FILE_SIZE || fileSize < 0)
  {
    std::cout << "File is too big: " << argv[1] << "\n";
    return 0;
  }

  //mapping file
  HANDLE hMapping = CreateFileMapping(inputFile, NULL, PAGE_READONLY, 0, 0, NULL);
  if(hMapping == NULL)
  {
    if(GetLastError() == ERROR_INVALID_PARAMETER)
      std::cout << "File doesn't exist: "<< argv[1] <<"\n";
    else if(GetLastError() == ERROR_ACCESS_DENIED)
      std::cout << "Access denied to file: " << argv[1] << "\n";
    else std::cout << "Can't specify error with code: " << GetLastError() << "\n";
    return 0;
  }
  //input text
  char* char_input =(char*)MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0);
  
  //creating and sorting vector
  std::vector<int> vector_for_sort;
  if(!createVectorFromChar(char_input, vector_for_sort))
  {
    std::cout << "Wrong input format" << "\n";
    return 0;
  }
  else
  {
    std::cout << "Start sorting\n";


    for(int i = 0; i < 1; i++)
    {

      insertionVectorSort(vector_for_sort);

      //if(average == 0)
      //  average = end - start;
      //else
      //  average = (average + end - start)/2;

    }
    std::cout << "End sorting\n";
  }



  UnmapViewOfFile(char_input);
	CloseHandle(hMapping);
	CloseHandle(inputFile);
  //end reading from file--------------->

  //<--------Writing into file
  str = argv[2];
  HANDLE output_file = CreateFile(s2ws(str).c_str(),GENERIC_WRITE|GENERIC_READ,0,NULL, CREATE_ALWAYS, 0,0);
  if(GetLastError() == ERROR_ACCESS_DENIED)
  {
    std::cout << "Can't access folder to create: " << argv[2] << "\n";
    return 0;
  }

  HANDLE output_mapping = CreateFileMapping(output_file, NULL, PAGE_READWRITE, 0, fileSize, 0);
  if(output_mapping == NULL)
  {
    std::cout << "CreateFileMapping returned null" << "\n Error: " << GetLastError() << "\n";
    return 0;
  }
  
  char* char_output = (char*)MapViewOfFile(output_mapping,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
  if(char_output == NULL)
  {
    std::cout <<"Unknown error with code: " << GetLastError() << "\n";
    return 0;
  }

  //writing vector into file
  int caret = 0;
  for(int i = 0; i < vector_for_sort.size(); i++){
    std::string temp = intToString(vector_for_sort[i]);
    memcpy(char_output + caret, temp.c_str(), temp.size());
    caret += temp.size();
  }

  UnmapViewOfFile(char_output);
	CloseHandle(output_mapping);
	CloseHandle(output_file);
  long end = time(0);
  duration_time.push_back(end - start);
  std::cout << "Average time is: " << end-start << "\n";
  //end writing into file------------->
}

